/***************************************************************************//**
 * @file CGI/AirportTerrain.cpp
 * @author  Marek M. Cel <marekcel@mscsim.org>
 *
 * @section LICENSE
 *
 * Copyright (C) 2014 Marek M. Cel
 *
 * This file is part of MScSim. You can redistribute and modify it under
 * the terms of GNU General Public License as published by the Free Software
 * Foundation; either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.
 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * Further information about the GNU General Public License can also be found
 * on the world wide web at http://www.gnu.org.
 ******************************************************************************/
#ifndef AIRPORT_TERRAIN_CPP
#define AIRPORT_TERRAIN_CPP
#endif

////////////////////////////////////////////////////////////////////////////////

#include <iostream>

#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Material>
#include <osg/ShapeDrawable>
#include <osg/Texture2D>

#include <osgDB/ReadFile>

#include <CGI/Airport.h>
#include <CGI/AirportTerrain.h>

////////////////////////////////////////////////////////////////////////////////

using namespace std;

////////////////////////////////////////////////////////////////////////////////

AirportTerrain::AirportTerrain( CGI_Data *data ) :
    m_data ( data )
{
    m_airportTerrainGroup = new osg::Group();
    m_airportTerrainGroup->setName( "AirportTerrainGroup" );

    createTerrain();
}

////////////////////////////////////////////////////////////////////////////////

void AirportTerrain::createTerrain()
{
    Airport::createSegment( m_airportTerrainGroup,
                                2000.0, -500.0, -2000.0, -25.0,
                                "data/textures/terrain/ground_1.png", true );

    Airport::createSegment( m_airportTerrainGroup,
                                2000.0, 325.0, -2000.0, 500.0,
                                "data/textures/terrain/ground_1.png", true );

    Airport::createSegment( m_airportTerrainGroup,
                                -2000.0, -500.0, -3000.0, 500.0,
                                "data/textures/terrain/ground_1.png", true );

    Airport::createSegment( m_airportTerrainGroup,
                                3000.0, -500.0, 2000.0, 500.0,
                                "data/textures/terrain/ground_1.png", true );

    createWall( -3000.0, 0.0, 0.0, 500.0, -4000.0, 0.0, 0.0, 800.0, 100.0 );
    createWall(  3000.0, 0.0, 0.0, 500.0,  4000.0, 0.0, 0.0, 800.0, 100.0 );
    createWall( 0.0, -500.0, 3000.0, 0.0, 0.0, -800.0, 4000.0, 0.0, 100.0 );
    createWall( 0.0,  500.0, 3000.0, 0.0, 0.0,  800.0, 4000.0, 0.0, 100.0 );
}

////////////////////////////////////////////////////////////////////////////////

void AirportTerrain::createWall( double ux, double uy, double uxw, double uyw,
                                     double lx, double ly, double lxw, double lyw,
                                     double alt )
{
    osg::ref_ptr<osg::Geode> wallGeode = new osg::Geode();
    wallGeode->setName( "wallGeode" );

    // geometry
    osg::ref_ptr<osg::Geometry> wallGeometry = new osg::Geometry();
    wallGeometry->setName( "wallGeometry" );

    osg::Vec3d p1( ux - uxw, uy - uyw, 0.0 );
    osg::Vec3d p2( ux + uxw, uy + uyw, 0.0 );
    osg::Vec3d p3( lx + lxw, ly + lyw, alt );
    osg::Vec3d p4( lx - lxw, ly - lyw, alt );

    osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
    osg::ref_ptr<osg::Vec2Array> texCoors = new osg::Vec2Array;

    vertices->push_back( p1 );
    vertices->push_back( p2 );
    vertices->push_back( p3 );
    vertices->push_back( p4 );

    texCoors->push_back( osg::Vec2( 0, 0 ) );
    texCoors->push_back( osg::Vec2( 1, 0 ) );
    texCoors->push_back( osg::Vec2( 1, 1 ) );
    texCoors->push_back( osg::Vec2( 0, 1 ) );

    wallGeometry->setVertexArray( vertices.get() );

    osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;

    osg::Vec3d v1 = p2 - p1;
    osg::Vec3d v2 = p3 - p1;
    osg::Vec3d n;

    if ( ux < 0.0 || uy > 0.0 )
        n = v2 ^ v1;
    else
        n = v1 ^ v2;

    n.normalize();

    normals->push_back( n );

    wallGeometry->setNormalArray( normals.get() );
    wallGeometry->setNormalBinding( osg::Geometry::BIND_OVERALL );

    wallGeometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::QUADS, 0, vertices->size() ) );

    // texture
    std::string textureFile = "data/textures/terrain/ground_2.png";
    osg::ref_ptr<osg::Image> wallImage = osgDB::readImageFile( textureFile );

    if ( wallImage.valid() )
    {
        osg::ref_ptr<osg::StateSet> wallStateSet = wallGeode->getOrCreateStateSet();

        wallGeometry->setTexCoordArray( 0, texCoors.get() );

        osg::ref_ptr<osg::Texture2D> wallTexture = new osg::Texture2D();
        wallTexture->setImage( wallImage.get() );

        wallTexture->setWrap( osg::Texture2D::WRAP_S, osg::Texture2D::REPEAT );
        wallTexture->setWrap( osg::Texture2D::WRAP_T, osg::Texture2D::REPEAT );

        wallTexture->setMaxAnisotropy( 8.0f );

        wallStateSet->setTextureAttributeAndModes( 0, wallTexture.get(), osg::StateAttribute::ON );
    }
    else
    {
        cerr << "ERROR! Cannot open texture file: " << textureFile << endl;
    }

    wallGeode->addDrawable( wallGeometry.get() );

//    // material
//    {
//        osg::ref_ptr<osg::StateSet> wallStateSet = wallGeode->getOrCreateStateSet();
//        osg::ref_ptr<osg::Material> segmentMaterial = new osg::Material();
//        segmentMaterial->setDiffuse( osg::Material::FRONT, osg::Vec4( 1.0f, 1.0f, 1.0f, 1.0f ) );
//        wallStateSet->setAttribute( segmentMaterial.get() );
//    }

    m_airportTerrainGroup->addChild( wallGeode.get() );
}
